#pragma once 
#include <queue>
#include <mutex>
#include <pthread.h>

namespace Tom
{
#define DEF_SIZE 10
    
    template<class T>
    class BlockQueue
    {
    public:
        BlockQueue(size_t cap = DEF_SIZE)
            :_cap(cap)
        {
            pthread_mutex_init(&_mtx, nullptr);
            pthread_cond_init(&_pro_cond, nullptr);
            pthread_cond_init(&_con_cond, nullptr);
        }
        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mtx);
            pthread_cond_destroy(&_pro_cond);
            pthread_cond_destroy(&_con_cond);
        }

        void Push(const T& inData)
        {
            pthread_mutex_lock(&_mtx);

            while(IsFull())
            {
                pthread_cond_wait(&_pro_cond, &_mtx);
            }
            _queue.push(inData);
            pthread_cond_signal(&_con_cond);
            pthread_mutex_unlock(&_mtx);
        }

        void Pop(T *outData)
        {
            pthread_mutex_lock(&_mtx);

            while(IsEmpty())
            {
                pthread_cond_wait(&_con_cond, &_mtx);
            }
            
            *outData = _queue.front();
            _queue.pop();

            pthread_cond_signal(&_pro_cond);
            pthread_mutex_unlock(&_mtx);

        }

    private:
        bool IsEmpty()
        {
            return _queue.empty();
        }
        bool IsFull()
        {
            return _queue.size() == _cap;
        }

    private:
        std::queue<T> _queue;
        size_t _cap;
        pthread_mutex_t _mtx;
        pthread_cond_t _pro_cond;
        pthread_cond_t _con_cond;
    };
}
